#include <vector>
#include <msclr/marshal_cppstd.h>
#include "BulletMachine.h"
#include "Callbacks.h"

using namespace std;
using namespace bs;
using namespace System;
using namespace System::Runtime::InteropServices;

namespace BulletScript
{

// ----------------------------------------------------------------------------
BulletMachine::BulletMachine(int screenWidth, int screenHeight)
	: mDisposed(false)
{
	mMachine = new Machine();
	mBatteries = new vector<Battery*>();
	
	Initialise(screenWidth, screenHeight);
}
// ----------------------------------------------------------------------------
BulletMachine::~BulletMachine()
{
	this->!BulletMachine();
}
// ----------------------------------------------------------------------------
BulletMachine::!BulletMachine()
{
	if (!mDisposed)
	{
		// Delete machine
		delete mMachine;

		// Delete batteries
		for (auto it = mBatteries->begin(); it != mBatteries->end(); ++it)
			delete *it;

		delete[] mBatteries;

		// Just do this once!
		mDisposed = true;
	}
}
// ----------------------------------------------------------------------------
void BulletMachine::Initialise(int screenWidth, int screenHeight)
{
	// Create types
	mMachine->createType("bullet");

	// Params: speed, angle
	mMachine->registerEmitFunction("bullet", "emitA", 2, bullet_emitAngle);

	// Params: speed, angle offset, target-x, target-y
	mMachine->registerEmitFunction("bullet", "emitT", 4, bullet_emitTarget);

	mMachine->setDieFunction("bullet", bullet_kill);

	// Property setters/getters
	mMachine->setAnchorX("bullet", bullet_setX, bullet_getX);
	mMachine->setAnchorY("bullet", bullet_setY, bullet_getY);
	mMachine->setAnchorAngle("bullet", bullet_setAngle, bullet_getAngle);
	mMachine->registerProperty("bullet", "speed", bullet_setSpeed, bullet_getSpeed);
	mMachine->registerProperty("bullet", "red", bullet_setRed, bullet_getRed);
	mMachine->registerProperty("bullet", "green", bullet_setGreen, bullet_getGreen);
	mMachine->registerProperty("bullet", "blue", bullet_setBlue, bullet_getBlue);
	mMachine->registerProperty("bullet", "alpha", bullet_setAlpha, bullet_getAlpha);

	// Native functions
	// ...

	// Globals
	mMachine->registerGlobalVariable("SCREEN_WIDTH", true, screenWidth);
	mMachine->registerGlobalVariable("SCREEN_HEIGHT", true, screenHeight);
	mMachine->registerGlobalVariable("PLAYER_X", true, 0.0f);
	mMachine->registerGlobalVariable("PLAYER_Y", true, 0.0f);
	mMachine->registerGlobalVariable("LEVEL_TIME", true, 0.0f);
}
// ----------------------------------------------------------------------------
void BulletMachine::Reset()
{
	mMachine->registerGlobalVariable("PLAYER_X", true, 0.0f);
	mMachine->registerGlobalVariable("PLAYER_Y", true, 0.0f);
	mMachine->registerGlobalVariable("LEVEL_TIME", true, 0.0f);
}
// ----------------------------------------------------------------------------
std::string BulletMachine::ConvertString(System::String^ str)
{
	msclr::interop::marshal_context context;
	return context.marshal_as<std::string>(str);
}
// ----------------------------------------------------------------------------
bool BulletMachine::CompileScript(System::String^ text)
{
	string stlstr = ConvertString(text);

	const char* cstr = stlstr.c_str();
	return mMachine->compileScript((uint8*)cstr, strlen(cstr)) == 0;
}
// ----------------------------------------------------------------------------
array<System::String^>^ BulletMachine::GetLogMessages()
{
	const Log& _log = mMachine->getLog();
				
	// Count log entries.
	int numEntries = 0;
	std::string msg = _log.getFirstEntry();
	while (msg != Log::END)
	{
		numEntries++;
		msg = _log.getNextEntry();
	}

	// Now allocate and read
	array<System::String^>^ entries = gcnew array<System::String^>(numEntries);

	int index = 0;
	msg = _log.getFirstEntry();
	while (msg != Log::END)
	{
		entries[index++] = gcnew System::String(msg.c_str());
		msg = _log.getNextEntry();
	}

	return entries;
}
// ----------------------------------------------------------------------------
BulletEmitter^ BulletMachine::CreateEmitter(System::String^ definition, float x, float y, float angle)
{
	return gcnew BulletEmitter(this->mMachine, definition, x, y, angle);
}
// ----------------------------------------------------------------------------
void BulletMachine::Update(float frameTime, float playerX, float playerY, float levelTime)
{
	mMachine->setGlobalVariableValue("PLAYER_X", playerX);
	mMachine->setGlobalVariableValue("PLAYER_Y", playerY);
	mMachine->setGlobalVariableValue("LEVEL_TIME", levelTime);

	mMachine->preUpdate(frameTime);

	for (auto it = mBatteries->begin(); it != mBatteries->end(); ++it)
		(*it)->Update(frameTime);

	mMachine->postUpdate(frameTime);
}
// ----------------------------------------------------------------------------

}